home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
prgtools
/
programm.ing
/
m2posx10.zoo
/
m2posix.10
/
src
/
file.ipp
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Modula Implementation
|
1993-12-13
|
36.0 KB
|
1,338 lines
IMPLEMENTATION MODULE file;
__IMP_SWITCHES__
#ifdef HM2
#ifdef __LONG_WHOLE__
(*$!i+: Modul muss mit $i- uebersetzt werden! *)
(*$!w+: Modul muss mit $w- uebersetzt werden! *)
#else
(*$!i-: Modul muss mit $i+ uebersetzt werden! *)
(*$!w-: Modul muss mit $w+ uebersetzt werden! *)
#endif
#endif
(*****************************************************************************)
(* Basiert auf der MiNTLIB von Eric R. Smith und anderen *)
(* --------------------------------------------------------------------------*)
(* 05-Dez-93, Holger Kleinschmidt *)
(*****************************************************************************)
VAL_INTRINSIC
CAST_IMPORT
PTR_ARITH_IMPORT
FROM SYSTEM IMPORT
(* TYPE *) ADDRESS,
(* PROC *) ADR;
FROM PORTAB IMPORT
(* CONST*) NULL,
(* TYPE *) SIGNEDWORD, UNSIGNEDWORD, SIGNEDLONG, UNSIGNEDLONG, WORDSET;
FROM MEMBLK IMPORT
(* PROC *) memalloc, memdealloc;
FROM OSCALLS IMPORT
(* PROC *) Fcreate, Fopen, Fclose, Fdelete, Fread, Fwrite, Fseek, Flock,
Fcntl, Fdup, Fforce, Pumask, Fchmod, Fattrib, Fchown, Fdatime,
Fpipe, Fxattr, Dgetdrv, Pgetuid, Pgetgid, Tgettime, Tgetdate,
Freadlink;
FROM ctype IMPORT
(* PROC *) tocard;
FROM cstr IMPORT
(* PROC *) strlen, AssignM2ToC;
FROM pSTRING IMPORT
(* PROC *) SLEN, APPEND;
FROM types IMPORT
(* CONST*) EOS, DDRVPOSTFIX, DDIRSEP,
(* TYPE *) PathName, uidT, gidT, inoT, timeT, offT, sizeT, ssizeT, devT, pidT,
StrPtr, StrRange, timeCast;
IMPORT e;
FROM DosSystem IMPORT
(* PROC *) DosVersion, FileLocking, MiNTVersion;
FROM DosSupport IMPORT
(* CONST*) FINDALL, XDECR, DINCR, MinHandle, MaxHandle, getmask, setmask,
(* TYPE *) DTA, FileAttributes, FileAttribute, DosFlags, DosFlag, HandleRange,
FileType,
(* VAR *) INODE, FD,
(* PROC *) IsTerm, IsDosDevice, UnixToDos, FindFirst, IsExec, DosToUnix;
(*==========================================================================*)
CONST
EOKL = LIC(0);
FSTAT = 00004600H;
BLKSIZE = 1024;
LBLKSIZE = 256; (* BLKSIZE DIV 4 *)
STDPERM = modeT{sIRUSR,sIWUSR,sIRGRP,sIWGRP,sIROTH,sIWOTH};
TYPE
XATTR = RECORD
mode : modeT;
index : UNSIGNEDLONG;
dev : UNSIGNEDWORD;
res1 : UNSIGNEDWORD;
nlink : UNSIGNEDWORD;
uid : UNSIGNEDWORD;
gid : UNSIGNEDWORD;
size : SIGNEDLONG;
blksize : SIGNEDLONG;
nblocks : SIGNEDLONG;
mtime : WORDSET;
mdate : WORDSET;
atime : WORDSET;
adate : WORDSET;
ctime : WORDSET;
cdate : WORDSET;
attr : WORDSET;
res2 : SIGNEDWORD;
res3 : ARRAY [0..1] OF SIGNEDLONG;
END;
CONST
FRDLCK = 0;
FWRLCK = 1;
FUNLCK = 3;
TYPE
FLOCK = RECORD
type : UNSIGNEDWORD;
whence : UNSIGNEDWORD;
start : SIGNEDLONG;
len : SIGNEDLONG;
pid : SIGNEDWORD;
END;
VAR
UMASK : modeT;
zerofill : ARRAY [0..LBLKSIZE-1] OF UNSIGNEDLONG;
MiNT : BOOLEAN;
VAR
DOSVersion : CARDINAL;
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
PROCEDURE open ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) access : OpenMode;
(* EIN/ -- *) mode : modeT ): INTEGER;
VAR res : INTEGER;
handle : INTEGER;
fd : HandleRange;
accMask : OpenMode;
attr : WORDSET;
lres : SIGNEDLONG;
done : BOOLEAN;
tty : BOOLEAN;
msize : CARDINAL;
stack : ADDRESS;
path0 : StrPtr;
BEGIN
(* Pfadname DOS-konform gestalten *)
msize := SLEN(file) + DINCR;
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(file, msize - DINCR, VAL(StrRange,msize), path0, tty, done);
IF NOT done THEN
memdealloc(stack);
RETURN(MINHANDLE-1);
END;
(* Flags ermitteln, die das OS selbst auswerten kann *)
IF MiNT THEN
accMask := oACCMODE + OpenMode{oAPPEND, oNONBLOCK, oCREAT, oTRUNC, oEXCL};
ELSE
accMask := oACCMODE;
END;
IF Fattrib(path0, 0, 0, attr) THEN
(* Datei existiert bereits *)
IF OpenMode{oCREAT, oEXCL} <= access THEN
(* Exklusiver Zugriff nicht moeglich *)
handle := e.EEXIST;
ELSE
(* Datei im angegebenen Modus oeffnen *)
done := Fopen(path0, access * accMask, handle);
IF NOT MiNT AND (oTRUNC IN access) AND (handle >= 0) THEN
(* TOS kann oTRUNC bei einer normalen Datei (kein Geraet) nicht
* selbst behandeln.
*)
done := Fclose(handle, res);
IF access * oACCMODE = oRDONLY THEN
(* Wenn die Datei nur zum Lesen geoeffnet wurde, ist kein
* Kuerzen moeglich.
*)
handle := e.EACCES;
ELSE
(* Sonst wird die Datei neu erzeugt und mit dem gewuenschten
* Zugriffsmodus geoeffnet. Die alten Dateiattribute werden
* uebernommen (auch faHIDDEN und faSYSTEM).
*
* Unter alten TOS-Versionen wurde nach einem 'Fcreate' eine
* alte Datei gleichen Namens manchmal nicht geloescht, deswegen
* zuerst das 'Fdelete'.
*)
done := Fdelete(path0, handle)
AND Fcreate(path0, 0, handle)
AND Fclose(handle, handle)
AND Fopen(path0, access * accMask, handle)
AND Fattrib(path0, 1, attr, attr);
END;
END;
END;
ELSIF oCREAT IN access THEN
(* Datei soll mit den angegebenen Attributen neu angelegt werden *)
mode := mode - UMASK;
(* Auch fuer MiNT, da 'Fchmod' die Prozessmaske nicht beruecksichtigt *)
IF MiNT THEN
(* oCREAT wird von MiNT erledigt *)
done := Fopen(path0, access * accMask, handle)
AND Fchmod(path0, mode, res);
ELSE
(* Sonst wird die Datei neu erzeugt und mit dem gewuenschten
* Zugriffsmodus geoeffnet. Fuer die Attribute der neuen Datei
* wird die Prozessmaske beruecksichtigt.
*)
IF sIWUSR IN mode THEN
attr := WORDSET{};
ELSE
attr := CAST(WORDSET,FileAttribute{faRDONLY});
END;
done := Fcreate(path0, 0, handle)
AND Fclose(handle, handle)
AND Fopen(path0, access * accMask, handle)
AND Fattrib(path0, 1, attr, attr);
END;
ELSE
(* Datei existiert nicht und soll auch nicht neu angelegt werden *)
handle := e.ENOENT;
END;
memdealloc(stack);
IF handle < MINHANDLE THEN
e.errno := handle;
RETURN(MINHANDLE-1);
END;
tty := IsTerm(handle);
IF MiNT THEN
(* Die kleinste Kennung, die 'Fopen' fuer eine Datei liefert,
* ist auch unter MiNT gleich 6. Falls aber eine kleinere Kennung
* frei ist, kann diese stattdessen benutzt werden. Also wird
* eine weitere Kennung fuer diese Datei erzeugt, und die kleinere
* der beiden verwendet, waehrend die andere wieder freigegeben wird.
* Eine andere Kennung veraendert nicht das Ergebnis von "IsTerm"!
*)
IF Fcntl(handle, 0, ORD(fDUPFD), lres) THEN
res := INT(lres);
IF res < handle THEN
(* Eine kleinere Kennung ist frei, also diese nehmen und die
* andere freigeben.
*)
done := Fclose(handle, handle);
handle := res;
ELSE
(* Die von 'Fopen' gelieferte Kennung ist bereits die kleinste
* freie gewesen, also die neue wieder freigeben.
*)
done := Fclose(res, res);
END;
END;
(* MiNT schliesst normalerweise alle Dateikennungen, ausser den
* Standardkanaelen, bei Ausfuehren eines 'Pexec'.
*)
done := Fcntl(handle, 0, ORD(fGETFD), lres);
IF ODD(lres) THEN
DEC(lres);
END;
done := Fcntl(handle, lres, ORD(fSETFD), lres);
IF tty THEN
FD[VAL(HandleRange,handle)].ftype := istty;
IF NOT (oNOCTTY IN access) AND NOT IsTerm(-1) THEN
(* Wenn Handle -1 (aktuelles Kontrollterminal) kein Terminal ist
* (auf /dev/null umgelenkt), aber die geoeffnete Datei, wird die neu
* geoffnete Datei zum Kontrollterminal, ausser, sowas ist unerwuenscht.
* (Kann nur unter MiNT auftreten.)
*)
done := Fforce(-1, handle, res);
END;
ELSE
FD[VAL(HandleRange,handle)].ftype := notty;
END;
ELSE
WITH FD[VAL(HandleRange,handle)] DO
cloex := FALSE;
IF tty THEN
ftype := istty;
ELSE
ftype := notty;
END;
flags := CAST(DosFlag,access);
END;
END; (* IF MiNT *)
RETURN(handle);
END open;
(*--------------------------------------------------------------------------*)
PROCEDURE creat ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) mode : modeT ): INTEGER;
BEGIN
RETURN(open(file, oWRONLY + OpenMode{oCREAT,oTRUNC}, mode));
END creat;
(*--------------------------------------------------------------------------*)
PROCEDURE fcntl ((* EIN/ -- *) h : INTEGER;
(* EIN/ -- *) cmd : FcntlCmd;
(* EIN/AUS *) VAR arg : FcntlArg ): INTEGER;
VAR done : BOOLEAN;
res : INTEGER;
lres : SIGNEDLONG;
lock : FLOCK;
par : SIGNEDLONG;
BEGIN
IF MiNT THEN
WITH arg DO
CASE cmd OF
fDUPFD : par := VAL(SIGNEDLONG,handle);
|fSETFD : par := VAL(SIGNEDLONG,CAST(UNSIGNEDWORD,fdflags));
|fSETFL : par := VAL(SIGNEDLONG,CAST(UNSIGNEDWORD,mode));
|fGETLK,
fSETLK,
fSETLKW : WITH flock DO WITH lock DO
IF lType <= fWRLCK THEN
type := VAL(UNSIGNEDWORD,lType);
ELSE
type := FUNLCK;
END;
whence := VAL(UNSIGNEDWORD,lWhence);
start := VAL(SIGNEDLONG,lStart);
len := VAL(SIGNEDLONG,lLen);
pid := VAL(SIGNEDWORD,lPid);
END; END;
par := CAST(SIGNEDLONG,ADR(lock));
ELSE (* fGETFD, fGETFL *)
par := 0;
END;
IF Fcntl(h, par, ORD(cmd), lres) THEN
CASE cmd OF
fDUPFD : handle := INT(lres);
FD[VAL(HandleRange,handle)] := FD[VAL(HandleRange,h)];
|fGETFD : fdflags := CAST(FDFlag,VAL(UNSIGNEDWORD,lres));
|fGETFL : mode := CAST(OpenMode,VAL(UNSIGNEDWORD,lres));
|fGETLK,
fSETLK,
fSETLKW : WITH flock DO WITH lock DO
IF type <= FWRLCK THEN
lType := VAL(LockType,type);
ELSE
lType := fUNLCK;
END;
lWhence := VAL(SeekMode,whence);
lStart := VAL(offT,start);
lLen := VAL(offT,len);
lPid := VAL(pidT,pid);
END; END;
ELSE
(* fSETFD, fSETFL *)
END;
RETURN(0);
ELSE
e.errno := INT(lres);
IF (e.errno = e.eLOCKED) OR (e.errno = e.eNSLOCK) THEN
e.errno := e.EACCES;
END;
RETURN(-1);
END;
END; (* WITH arg *)
ELSE (* NOT MiNT *)
IF (h<MinHandle) OR (h>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
END;
WITH arg DO
CASE cmd OF
fSETFD : FD[VAL(HandleRange,h)].cloex := FdCloExec IN fdflags;
|fGETFD : IF FD[VAL(HandleRange,h)].cloex THEN
fdflags := FDFlag{FdCloExec};
ELSE
fdflags := FDFlag{};
END;
|fSETFL : WITH FD[VAL(HandleRange,h)] DO
flags := flags * setmask + (CAST(DosFlag,mode) - setmask);
END;
|fGETFL : mode := CAST(OpenMode,FD[VAL(HandleRange,h)].flags * getmask);
|fDUPFD : IF Fdup(h, handle) THEN
FD[VAL(HandleRange,handle)] := FD[VAL(HandleRange,h)];
FD[VAL(HandleRange,handle)].cloex := FALSE;
ELSE
e.errno := handle;
RETURN(-1);
END;
|fSETLK : WITH flock DO
res := e.EINVAL;
IF NOT FileLocking()
OR (lType = fRDLCK) OR (lWhence <> SeekSet)
OR NOT Flock(h, ORD(lType), lStart, lLen, res)
THEN
IF (res = e.eLOCKED) OR (res = e.eNSLOCK) THEN
e.errno := e.EACCES;
ELSE
e.errno := res;
END;
RETURN(-1);
END;
END;
ELSE
e.errno := e.EINVAL;
RETURN(-1);
END; (* CASE *)
RETURN(0);
END; (* WITH arg *)
END; (* IF MiNT *)
END fcntl;
(*--------------------------------------------------------------------------*)
PROCEDURE close ((* EIN/ -- *) h : INTEGER ): INTEGER;
VAR res : INTEGER;
BEGIN
IF (h<MinHandle) OR (h>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
END;
IF Fclose(h, res) THEN
WITH FD[VAL(HandleRange,h)] DO
ftype := unknown;
cloex := FALSE;
END;
RETURN(0);
ELSE
e.errno := res;
RETURN(-1);
END;
END close;
(*--------------------------------------------------------------------------*)
PROCEDURE read ((* EIN/ -- *) h : INTEGER;
(* EIN/ -- *) buf : ADDRESS;
(* EIN/ -- *) len : sizeT ): ssizeT;
VAR lres : SIGNEDLONG;
BEGIN
IF Fread(h, VAL(SIGNEDLONG,len), buf, lres) THEN
RETURN(VAL(ssizeT,lres));
ELSE
e.errno := INT(lres);
RETURN(-1);
END;
END read;
(*--------------------------------------------------------------------------*)
PROCEDURE write ((* EIN/ -- *) h : INTEGER;
(* EIN/ -- *) buf : ADDRESS;
(* EIN/ -- *) len : sizeT ): ssizeT;
VAR lres : SIGNEDLONG;
BEGIN
IF NOT MiNT THEN
IF (h<MinHandle) OR (h>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
ELSIF append IN FD[VAL(HandleRange,h)].flags THEN
IF NOT Fseek(0, h, ORD(SeekEnd), lres) THEN
e.errno := INT(lres);
RETURN(-1);
END;
END;
END;
IF Fwrite(h, VAL(SIGNEDLONG,len), buf, lres) THEN
RETURN(VAL(ssizeT,lres));
ELSE
e.errno := INT(lres);
RETURN(-1);
END;
END write;
(*--------------------------------------------------------------------------*)
PROCEDURE lseek ((* EIN/ -- *) h : INTEGER;
(* EIN/ -- *) off : offT;
(* EIN/ -- *) mode : SeekMode ): offT;
CONST ERANGEL = LIC(-64);
EACCDNL = LIC(-36);
VAR lres : SIGNEDLONG;
curPos : SIGNEDLONG;
newPos : SIGNEDLONG;
len : SIGNEDLONG;
done : BOOLEAN;
BEGIN
len := VAL(SIGNEDLONG,off);
IF len <= LIC(0) THEN
(* Datei braucht nicht verlaengert zu werden *)
IF Fseek(len, h, ORD(mode), lres) THEN
RETURN(VAL(offT,lres));
ELSIF MiNT AND (lres = EACCDNL) THEN
e.errno := e.ESPIPE;
ELSE
e.errno := INT(lres);
END;
RETURN(-1);
END;
(* Augenblickliche Position feststellen, bei 'SeekEnd' gleich
* ans Ende der Datei.
*)
IF mode = SeekEnd THEN
done := Fseek(0, h, ORD(SeekEnd), curPos);
ELSE
done := Fseek(0, h, ORD(SeekCur), curPos);
END;
IF NOT done THEN
IF MiNT AND (curPos = EACCDNL) THEN
e.errno := e.ESPIPE;
ELSE
e.errno := INT(curPos);
END;
RETURN(-1);
END;
(* gewuenschte Position berechnen. 'SeekEnd' und 'SeekCur' koennen
* gleichbehandelt werden, da der Zeiger bei 'SeekEnd' schon am
* Ende der Datei steht.
*)
IF mode = SeekSet THEN
newPos := len;
ELSE
newPos := curPos + len;
END;
(* Es kann sein (ist auch meistens der Fall), dass die gewuenschte
* Position innerhalb der bestehenden Datei liegt. Deswegen wird zuerst
* versucht, die gewuenschte Position direkt anzufahren. Wenn dabei ein
* ``Range-Fehler'' auftritt, muss die Datei verlaengert werden.
* Ein ``Range-Fehler'' tritt nicht auf, wenn das Dateisystem
* (z.B. MinixFS) ein Fseek hinter das Dateiende selbst verwaltet.
*)
done := Fseek(len, h, ORD(mode), curPos);
IF curPos = newPos THEN
RETURN(VAL(offT,curPos));
ELSIF NOT done AND (curPos <> ERANGEL) THEN
e.errno := INT(curPos);
RETURN(-1);
END;
done := Fseek(0, h, ORD(SeekEnd), curPos);
(* Solange Nullbytes schreiben, bis die Datei auf die gewuenschte
* Laenge gebracht ist.
*)
REPEAT
len := newPos - curPos;
IF len > VAL(SIGNEDLONG,BLKSIZE) THEN
len := VAL(SIGNEDLONG,BLKSIZE);
END;
done := Fwrite(h, len, ADR(zerofill), lres);
IF lres <> len THEN
IF done THEN
RETURN(VAL(offT,curPos + lres));
ELSE
e.errno := INT(lres);
RETURN(VAL(offT,curPos));
END;
END;
INC(curPos, len);
UNTIL curPos >= newPos;
RETURN(VAL(offT,curPos));
END lseek;
(*--------------------------------------------------------------------------*)
PROCEDURE ftruncate ((* EIN/ -- *) h : INTEGER;
(* EIN/ -- *) len : offT ): INTEGER;
(* MinixFS 0.60pl6 funktioniert nur, wenn die Datei mit oWRONLY
geoeffnet wurde.
*)
CONST FTRUNCATE = 4604H; (* ('F'<<8)|4 *)
VAR lres : SIGNEDLONG;
BEGIN
IF MiNT THEN
IF Fcntl(h, ADR(len), FTRUNCATE, lres) THEN
RETURN(0);
ELSE
e.errno := INT(lres);
RETURN(-1);
END;
ELSE
e.errno := e.ENOSYS;
RETURN(-1);
END;
END ftruncate;
(*--------------------------------------------------------------------------*)
PROCEDURE dup ((* EIN/ -- *) h : INTEGER ): INTEGER;
VAR lres : SIGNEDLONG;
done : BOOLEAN;
newh : INTEGER;
BEGIN
IF MiNT THEN
done := Fcntl(h, 0, ORD(fDUPFD), lres);
newh := INT(lres);
IF done THEN
(* 'FdCloExec'-Flag loeschen, falls gesetzt *)
done := Fcntl(newh, 0, ORD(fGETFD), lres);
IF ODD(lres) THEN
DEC(lres);
END;
done := Fcntl(newh, lres, ORD(fSETFD), lres);
FD[VAL(HandleRange,newh)].ftype := FD[VAL(HandleRange,h)].ftype;
RETURN(newh);
ELSE
e.errno := newh;
RETURN(-1);
END;
ELSE
IF (h<MinHandle) OR (h>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
END;
IF Fdup(h, newh) THEN
FD[VAL(HandleRange,newh)] := FD[VAL(HandleRange,h)];
FD[VAL(HandleRange,newh)].cloex := FALSE;
RETURN(newh);
ELSE
e.errno := newh;
RETURN(-1);
END;
END;
END dup;
(*--------------------------------------------------------------------------*)
PROCEDURE dup2 ((* EIN/ -- *) oldh : INTEGER;
(* EIN/ -- *) newh : INTEGER ): INTEGER;
VAR res : INTEGER;
lres : SIGNEDLONG;
void : BOOLEAN;
BEGIN
IF oldh = newh THEN
RETURN(newh);
END;
(* Das Schliessen eines Standardkanals macht eine vorherige
* Umleitung rueckgaengig. Ist aber erst seit dem GEMDOS des TOS 1.04
* anwendbar.
*)
IF DOSVersion >= 1500H THEN
void := Fclose(newh, res);
END;
IF Fforce(newh, oldh, res) THEN
IF MiNT THEN
(* 'FdCloExec'-Flag loeschen, falls gesetzt *)
void := Fcntl(newh, 0, ORD(fGETFD), lres);
IF ODD(lres) THEN
DEC(lres);
END;
void := Fcntl(newh, lres, ORD(fSETFD), lres);
FD[VAL(HandleRange,newh)].ftype := FD[VAL(HandleRange,oldh)].ftype;
ELSE
IF (newh<MinHandle) OR (newh>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
END;
FD[VAL(HandleRange,newh)] := FD[VAL(HandleRange,oldh)];
FD[VAL(HandleRange,newh)].cloex := FALSE;
END;
RETURN(newh);
ELSE
e.errno := res;
RETURN(-1);
END;
END dup2;
(*--------------------------------------------------------------------------*)
PROCEDURE umask ((* EIN/ -- *) excl : modeT ): modeT;
VAR oldmask : modeT;
BEGIN
oldmask := UMASK;
UMASK := excl;
IF MiNT THEN
RETURN(CAST(modeT,Pumask(excl)));
ELSE
RETURN(oldmask);
END;
END umask;
(*---------------------------------------------------------------------------*)
PROCEDURE chmod ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) mode : modeT ): INTEGER;
VAR res : INTEGER;
dot : BOOLEAN;
done : BOOLEAN;
dta : DTA;
__REG__ attr : FileAttribute;
old : WORDSET;
stack : ADDRESS;
msize : CARDINAL;
path0 : StrPtr;
BEGIN
msize := SLEN(file) + DINCR;
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(file, msize - DINCR, VAL(StrRange,msize), path0, dot, done);
IF NOT done THEN
memdealloc(stack);
RETURN(-1);
END;
IF MiNT THEN
IF Fchmod(path0, mode, res) THEN
res := 0;
ELSE
e.errno := res;
res := -1;
END;
memdealloc(stack);
RETURN(res);
ELSIF FindFirst(path0, FINDALL, dta, res) THEN
attr := dta.attr;
IF faSUBDIR IN attr THEN
(* Verzeichnisse in Ruhe lassen (duerfen keine weiteren Attribute haben)*)
memdealloc(stack);
RETURN(0);
END;
IF faCHANGED IN attr THEN
(* Archivbit nicht veraendern *)
attr := FileAttribute{faRDONLY, faCHANGED};
ELSE
attr := FileAttribute{faRDONLY};
END;
IF sIWUSR IN mode THEN
EXCL(attr, faRDONLY);
END;
IF Fattrib(path0, 1, attr, old) THEN
res := 0;
ELSE
e.errno := INT(CAST(SIGNEDWORD,old));
res := -1;
END;
ELSE
e.errno := res;
res := -1;
END;
memdealloc(stack);
RETURN(res);
END chmod;
(*--------------------------------------------------------------------------*)
PROCEDURE chown ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) uid : uidT;
(* EIN/ -- *) gid : gidT ): INTEGER;
VAR res : INTEGER;
dot : BOOLEAN;
done : BOOLEAN;
stack : ADDRESS;
msize : CARDINAL;
path0 : StrPtr;
BEGIN
IF MiNT THEN
msize := SLEN(file) + DINCR;
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(file, msize - DINCR, VAL(StrRange,msize), path0, dot, done);
IF NOT done THEN
res := -1;
ELSE
IF Fchown(path0, uid, gid, res) THEN
res := 0;
ELSE
e.errno := res;
res := -1;
END;
END;
memdealloc(stack);
RETURN(res);
ELSIF (uid = 0) AND (gid = 0) THEN
RETURN(0);
ELSE
e.errno := e.EINVAL;
RETURN(-1);
END;
END chown;
(*--------------------------------------------------------------------------*)
PROCEDURE utime ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) times : UTimeBuf ): INTEGER;
CONST FUTIME = 4603H; (* ('F'<<8)|3 *)
VAR lres : SIGNEDLONG;
hndl : INTEGER;
void : BOOLEAN;
done : BOOLEAN;
tmp : WORDSET;
stack : ADDRESS;
tptr : ADDRESS;
msize : CARDINAL;
path0 : StrPtr;
tc : timeCast;
BEGIN
msize := SLEN(file) + DINCR;
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(file, msize - DINCR, VAL(StrRange,msize), path0, void, done);
IF NOT done THEN
memdealloc(stack);
RETURN(-1);
END;
tptr := ADR(times);
WITH times DO
IF modtime = VAL(UNSIGNEDLONG,0) THEN
tptr := NULL;
tc.date := Tgetdate();
tc.time := Tgettime();
modtime := tc.cmp;
actime := tc.cmp;
END;
(* bei 'timeT' sind 'date' und 'time' vertauscht *)
tc.cmp := modtime;
tmp := tc.date;
tc.date := tc.time;
tc.time := tmp;
modtime := tc.cmp;
tc.cmp := actime;
tmp := tc.date;
tc.date := tc.time;
tc.time := tmp;
actime := tc.cmp;
done := Fopen(path0, oWRONLY, hndl);
IF done THEN
IF NOT (MiNT AND Fcntl(hndl, tptr, FUTIME, lres)) THEN
Fdatime(ADR(modtime), hndl, 1);
END;
void := Fclose(hndl, hndl);
ELSIF hndl = e.eFILNF THEN
void := Fattrib(path0, 0, 0, tmp);
IF faSUBDIR IN CAST(FileAttribute,tmp) THEN
(* Verzeichnisse in Ruhe lassen *)
done := TRUE;
END;
END;
END; (* WITH *)
IF done THEN
hndl := 0;
ELSE
e.errno := hndl;
hndl := -1;
END;
memdealloc(stack);
RETURN(hndl);
END utime;
(*---------------------------------------------------------------------------*)
PROCEDURE pipe ((* -- /AUS *) VAR ph : PipeBuf ): INTEGER;
VAR handle : ARRAY [0..1] OF SIGNEDWORD;
res : INTEGER;
BEGIN
ph.readh := 0;
ph.writeh := 0;
IF MiNT THEN
IF Fpipe(ADR(handle), res) THEN
ph.readh := INT(handle[0]);
ph.writeh := INT(handle[1]);
FD[handle[0]].ftype := notty;
FD[handle[1]].ftype := notty;
RETURN(0);
ELSE
e.errno := res;
RETURN(-1);
END;
ELSE
e.errno := e.ENOSYS;
RETURN(-1);
END;
END pipe;
(*---------------------------------------------------------------------------*)
PROCEDURE MiNTstat ( hndl : BOOLEAN;
sym : BOOLEAN;
h : INTEGER;
path : StrPtr;
VAR st : StatRec ): INTEGER;
VAR
lres : SIGNEDLONG;
done : BOOLEAN;
dlen : INTEGER;
xlen : INTEGER;
xattr : XATTR;
tc : timeCast;
pre : ARRAY [0..10] OF CHAR;
(* Es reicht, die ersten Zeichen des DOS-Pfades zu kennen, um zu
berechnen, wieviele Zeichen laenger oder kuerzer der *IX-Pfad ist,
da "DosToUnix()" nur am Beginn des Pfades Laengen-Veraenderungen
vornimmt. Dadurch gibt es keine Begrenzung der Pfadlaenge auch ohne
dass deswegen ein riesiger Puffer bereitgestellt werden muss.
*)
BEGIN
IF hndl THEN
done := Fcntl(h, ADR(xattr), FSTAT, lres);
h := INT(lres);
ELSE
done := Fxattr(ORD(sym), path, ADR(xattr), h);
IF sym AND done AND (xattr.mode * sIFMT = sIFLNK) THEN
done := Freadlink(10, ADR(pre), path, h) OR (h = e.eRANGE);
(* eRANGE bedeutet nur, dass das ungekuerzte Ergebnis laenger als 10
* Zeichen waere, aber das interessiert hier nicht.
*)
IF done THEN
DosToUnix(CAST(StrPtr,ADR(pre)),
0, CAST(StrPtr,ADR(pre)),
dlen,
xlen);
INC(xattr.size, VAL(SIGNEDLONG,xlen - dlen));
END;
END;
END;
IF NOT done THEN
e.errno := h;
RETURN(-1);
END;
WITH st DO WITH xattr DO
stMode := mode;
stIno := index;
stDev := dev;
stNlink := nlink;
stUid := uid;
stGid := gid;
stSize := size;
tc.time := mtime;
tc.date := mdate;
stMtime := tc.cmp;
tc.time := atime;
tc.date := adate;
stAtime := tc.cmp;
tc.time := ctime;
tc.date := cdate;
stCtime := tc.cmp;
END; END;
RETURN(0);
END MiNTstat;
(*--------------------------------------------------------------------------*)
PROCEDURE istat (VAR name : ARRAY OF CHAR;
VAR st : StatRec;
sym : BOOLEAN ): INTEGER;
CONST DIRSIZE = 1024;
VAR dta : DTA;
err : INTEGER;
__REG__ pLen : UNSIGNEDWORD;
ROOT : BOOLEAN;
DOT : BOOLEAN;
drv : BOOLEAN;
stack : ADDRESS;
msize : CARDINAL;
path0 : StrPtr;
tc : timeCast;
BEGIN
msize := SLEN(name) + DINCR + 4; (* + 4 wegen ++ "\*.*" *)
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(name, msize - DINCR - 4, VAL(StrRange,msize), path0, DOT, drv);
IF NOT drv THEN
memdealloc(stack);
RETURN(-1);
END;
IF MiNT THEN
err := MiNTstat(FALSE, sym, 0, path0, st);
memdealloc(stack);
RETURN(err);
END;
pLen := VAL(UNSIGNEDWORD,strlen(path0));
st.stUid := 0;
st.stGid := 0;
IF IsDosDevice(path0) THEN
WITH st DO
stIno := VAL(inoT,INODE); INC(INODE);
stMode := sIFCHR + STDPERM;
stDev := 0;
tc.time := Tgettime();
tc.date := Tgetdate();
stMtime := tc.cmp;
stAtime := tc.cmp;
stCtime := tc.cmp;
stNlink := 1;
stSize := 0;
END;
memdealloc(stack);
RETURN(0);
END;
IF path0^[1] = DDRVPOSTFIX THEN
st.stDev := VAL(devT,tocard(path0^[0]) - 10);
drv := TRUE;
ELSE
st.stDev := VAL(devT,Dgetdrv());
drv := FALSE;
END;
(* Hauptverzeichnisse muessen gesondert behandelt werden, da sie nicht
* wie Unterverzeichnisse in der Baumstruktur eingebunden sind - sie
* haben kein Erstellungsdatum und besitzen nicht die Eintraege
* "." und ".." zur Verkettung.
*)
IF (pLen = 1) AND (path0^[0] = DDIRSEP)
OR drv AND (pLen = 3) AND (path0^[2] = DDIRSEP)
THEN
(* Ein Hauptverzeichnis ist direkt angegeben, deshalb sind keine
* weiteren Tests noetig.
*)
ROOT := TRUE;
ELSE
IF path0^[pLen-1] = DDIRSEP THEN
(* Verzeichnisse nicht extra kennzeichnen.
* 'pLen' ist mindestens zwei, da der Fall 'pLen' = 1
* oben abgefangen wird.
*)
path0^[pLen-1] := 0C;
DEC(pLen);
ELSIF drv AND (pLen = 2) THEN
(* "Fsfirst("x:")" funktioniert nicht *)
path0^[2] := '.';
path0^[3] := 0C;
DOT := TRUE;
INC(pLen);
END;
IF DOT THEN
AssignM2ToC("\*.*", msize - VAL(CARDINAL,pLen), ADDADR(path0, pLen));
(* Den ersten Eintrag suchen, sodass bei allen Verzeichnissen - ausser
* den Hauptverzeichnissen - der Eintrag "." gefunden wird.
* (Bei "..\*.*" wird das "." des uebergeordneten Verzeichnisses
* gefunden.)
*)
END;
IF FindFirst(path0, FINDALL, dta, err) THEN
ROOT := DOT AND ((dta.name[0] <> '.') OR (dta.name[1] <> 0C));
(* nicht-leeres Hauptverzeichnis, falls der erste Eintrag nicht
* mit einem Punkt beginnt (normaler Dateiname), oder nach dem Punkt
* nicht beendet ist (dann kann es nicht "." sein, das in allen
* Verzeichnissen zuerst steht.
*)
ELSE
(* Wenn kein Eintrag gefunden wird und "." oder ".." angegeben
* wurden, handelt es sich um ein leeres Hauptverzeichnis,
* ansonsten ist ein Fehler aufgetreten (angegebene Datei wurde
* nicht gefunden).
*)
IF DOT AND (err = e.eFILNF) THEN
ROOT := TRUE;
ELSE
e.errno := err;
memdealloc(stack);
RETURN(-1);
END;
END;
END;
IF ROOT THEN
(* Einem Hauptverzeichnis lassen sich leider kaum Informationen
* entlocken.
*)
WITH st DO
stIno := 2;
stSize := DIRSIZE;
stNlink := 2;
stMode := sIFDIR + STDPERM + modeT{sIXUSR, sIXGRP, sIXOTH};
stMtime := 0;
stAtime := 0;
stCtime := 0;
END;
memdealloc(stack);
RETURN(0);
END;
WITH st DO
stIno := VAL(inoT,INODE); INC(INODE);
tc.date := dta.date;
tc.time := dta.time;
stMtime := tc.cmp;
stAtime := tc.cmp;
stCtime := tc.cmp;
IF faSUBDIR IN dta.attr THEN
stSize := DIRSIZE;
stNlink := 2;
ELSE
stSize := dta.size;
stNlink := 1;
END;
IF faSUBDIR IN dta.attr THEN
stMode := sIFDIR + STDPERM + modeT{sIXUSR, sIXGRP, sIXOTH};
ELSIF IsExec(path0) THEN
stMode := sIFREG + STDPERM + modeT{sIXUSR, sIXGRP, sIXOTH};
ELSE
stMode := sIFREG + STDPERM;
END;
IF faRDONLY IN dta.attr THEN
stMode := stMode - modeT{sIWUSR, sIWGRP, sIWOTH};
END;
END; (* WITH st *)
memdealloc(stack);
RETURN(0);
END istat;
(*--------------------------------------------------------------------------*)
PROCEDURE stat ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* -- /AUS *) VAR st : StatRec ): INTEGER;
BEGIN
RETURN(istat(file, st, FALSE));
END stat;
(*--------------------------------------------------------------------------*)
PROCEDURE lstat ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* -- /AUS *) VAR st : StatRec ): INTEGER;
BEGIN
RETURN(istat(file, st, TRUE));
END lstat;
(*--------------------------------------------------------------------------*)
PROCEDURE fstat ((* EIN/ -- *) h : INTEGER;
(* -- /AUS *) VAR st : StatRec ): INTEGER;
VAR err : INTEGER;
pos : SIGNEDLONG;
size : SIGNEDLONG;
__REG__ void : BOOLEAN;
time : ARRAY [0..1] OF WORDSET;
lres : SIGNEDLONG;
magic : UNSIGNEDWORD;
dummy : StrPtr;
tc : timeCast;
BEGIN
IF MiNT THEN
RETURN(MiNTstat(TRUE, FALSE, h, dummy, st));
END;
IF (h<MinHandle) OR (h>MaxHandle) THEN
e.errno := e.EBADF;
RETURN(-1);
END;
WITH FD[VAL(HandleRange,h)] DO
IF ftype = unknown THEN
IF IsTerm(h) THEN
ftype := istty;
ELSE
ftype := notty;
END;
END;
END;
WITH st DO
IF FD[VAL(HandleRange,h)].ftype = istty THEN
stMode := sIFCHR + STDPERM;
stSize := 0;
tc.time := Tgettime();
tc.date := Tgetdate();
ELSE
Fdatime(ADR(time), h, 0);
tc.time := time[0];
tc.date := time[1];
IF Fseek(0, h, ORD(SeekCur), pos) THEN
void := Fseek(0, h, ORD(SeekEnd), size);
stSize := size;
void := Fseek(0, h, ORD(SeekSet), size);
void := Fread(h, 2, ADR(magic), lres);
IF (lres = LIC(2)) AND ((magic = 601AH) OR (magic = 2321H))(* #! *) THEN
stMode := sIFREG + STDPERM + modeT{sIXUSR, sIXGRP, sIXOTH};
ELSE
stMode := sIFREG + STDPERM;
END;
void := Fseek(pos, h, ORD(SeekSet), size);
ELSE
e.errno := e.EBADF;
RETURN(-1);
END;
END;
stMtime := tc.cmp;
stAtime := tc.cmp;
stCtime := tc.cmp;
stUid := 0;
stGid := 0;
stDev := VAL(devT,Dgetdrv());
stNlink := 1;
stIno := VAL(inoT,INODE); INC(INODE);
END; (* WITH *)
RETURN(0);
END fstat;
(*--------------------------------------------------------------------------*)
PROCEDURE sISCHR ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFCHR);
END sISCHR;
(*--------------------------------------------------------------------------*)
PROCEDURE sISDIR ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFDIR);
END sISDIR;
(*--------------------------------------------------------------------------*)
PROCEDURE sISBLK ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFBLK);
END sISBLK;
(*--------------------------------------------------------------------------*)
PROCEDURE sISREG ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFREG);
END sISREG;
(*--------------------------------------------------------------------------*)
PROCEDURE sISFIFO ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFIFO);
END sISFIFO;
(*--------------------------------------------------------------------------*)
PROCEDURE sISLNK ((* EIN/ -- *) stMode : modeT ): BOOLEAN;
BEGIN
RETURN(stMode * sIFMT = sIFLNK);
END sISLNK;
(*--------------------------------------------------------------------------*)
PROCEDURE access ((* EIN/ -- *) REF file : ARRAY OF CHAR;
(* EIN/ -- *) acc : AccessMode ): INTEGER;
VAR dta : DTA;
st : StatRec;
BEGIN
IF istat(file, st, FALSE) < 0 THEN
RETURN(-1);
ELSIF acc = fOK THEN
RETURN(0);
END;
IF NOT MiNT OR (Pgetuid() = st.stUid) THEN
IF acc <= CAST(AccessMode,VAL(UNSIGNEDWORD,
CAST(UNSIGNEDWORD,st.stMode * sIRWXU) DIV 64))
THEN
RETURN(0);
ELSE
e.errno := e.EACCES;
RETURN(-1);
END;
END;
IF Pgetgid() = st.stGid THEN
IF acc <= CAST(AccessMode,VAL(UNSIGNEDWORD,
CAST(UNSIGNEDWORD,st.stMode * sIRWXG) DIV 8))
THEN
RETURN(0);
ELSE
e.errno := e.EACCES;
RETURN(-1);
END;
END;
IF acc <= CAST(AccessMode,st.stMode * sIRWXO) THEN
RETURN(0);
ELSE
e.errno := e.EACCES;
RETURN(-1);
END;
END access;
(*==========================================================================*)
VAR
i : CARDINAL;
old : WORDSET;
BEGIN (* file *)
FOR i := 0 TO LBLKSIZE - 1 DO
zerofill[i] := 0;
END;
DOSVersion := DosVersion();
MiNT := MiNTVersion() > 0;
IF MiNT THEN
UMASK := CAST(modeT,Pumask(0));
old := Pumask(UMASK);
ELSE
UMASK := modeT{};
END;
END file.